home *** CD-ROM | disk | FTP | other *** search
- Subject: v07i031: Top users display for 4.2BSD, Version 2.0, Part02/02
- Newsgroups: mod.sources
- Approved: mirror!rs
-
- Submitted by: William LeFebvre <phil@rice.edu>
- Mod.sources: Volume 7, Issue 31
- Archive-name: top2/Part02
-
-
- #!/bin/sh
- # This is a shell archive. Remove anything before this line,
- # then unpack it by saving it in a file and typing "sh file".
- # If all goes well, you will see the message "No problems found."
-
- # Exit status; set to 1 on "wc" errors or if would overwrite.
- STATUS=0
- # Contents: top.c top.local.h top.man MANIFEST
-
- echo x - top.c
- if test -f top.c ; then
- echo top.c exists, putting output in $$top.c
- OUT=$$top.c
- STATUS=1
- else
- OUT=top.c
- fi
- sed 's/^X//' > $OUT <<'@//E*O*F top.c//'
- Xchar *copyright =
- X "Top, version 2.0, copyright (c) 1984, 1986, William LeFebvre";
-
- X/*
- X * Top users display for Berkeley Unix
- X * Version 2.0
- X *
- X * This program may be freely redistributed to other Unix sites, but this
- X * entire comment MUST remain intact.
- X *
- X * Copyright (c) 1984, 1986, William LeFebvre, Rice University
- X *
- X * This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
- X * Compile with the preprocessor constant "FOUR_ONE" set to get an
- X * executable that will run on Berkeley 4.1 Unix.
- X *
- X * The Sun kernel uses scaled integers instead of floating point.
- X * Compilation with the preprocessor variable "sun" gets an executable
- X * that will run on Sun Unix version 1.1 or later ("sun" is automatically
- X * set by the Sun C compiler).
- X *
- X * The Pyramid splits the stack size (p_ssize) into control stack and user
- X * stack sizes. Compilation with the preprocessor variable "pyr" gets an
- X * executable that will run on Pyramids ("pyr" is automatically set by the
- X * Pyramid C compiler).
- X *
- X * See the file "Changes" for more information on version-to-version changes.
- X */
-
- X#include <stdio.h>
- X#include <pwd.h>
- X#include <nlist.h>
- X#include <signal.h>
- X#include <setjmp.h>
- X#include <sys/param.h>
- X#include <sys/dir.h>
- X#include <sys/user.h>
- X#include <sys/proc.h>
- X#include <sys/dk.h>
- X#include <sys/vm.h>
-
- X/* includes specific to top */
- X#include "layout.h"
- X#include "screen.h" /* interface to screen package */
- X#include "top.h"
- X#include "top.local.h"
- X#include "boolean.h"
-
- X/* Special atoi routine returns either a non-negative number or one of: */
- X#define Infinity -1
- X#define Invalid -2
-
- X/* Size of the stdio buffer given to stdout */
- X#define Buffersize 2048
-
- X/* The buffer the stdio will use */
- Xchar stdoutbuf[Buffersize];
-
- X/* wish list for kernel symbols */
-
- Xstruct nlist nlst[] = {
- X { "_avenrun" },
- X#define X_AVENRUN 0
- X { "_ccpu" },
- X#define X_CCPU 1
- X { "_cp_time" },
- X#define X_CP_TIME 2
- X { "_hz" },
- X#define X_HZ 3
- X { "_mpid" },
- X#define X_MPID 4
- X { "_nproc" },
- X#define X_NPROC 5
- X { "_proc" },
- X#define X_PROC 6
- X { "_total" },
- X#define X_TOTAL 7
- X { 0 },
- X};
-
- X/* build Signal masks */
- X#define Smask(s) (1 << ((s) - 1))
-
- X/* for system errors */
- Xextern int errno;
-
- X/* for getopt: */
- Xextern int optind;
- Xextern char *optarg;
-
- X/* signal handling routines */
- Xint leave();
- Xint onalrm();
- Xint tstop();
-
- Xint nproc;
- Xint mpid;
-
- X/* kernel "hz" variable -- clock rate */
- Xlong hz;
-
- X/* All this is to calculate the cpu state percentages */
-
- Xlong cp_time[CPUSTATES];
- Xlong cp_old[CPUSTATES];
- Xlong cp_change[CPUSTATES];
- Xlong total_change;
- Xlong mpid_offset;
- Xlong avenrun_offset;
- Xlong cp_time_offset;
- Xlong total_offset;
-
- X#ifdef sun
- Xlong ccpu;
- Xlong avenrun[3];
- X#else
- Xdouble ccpu;
- Xdouble avenrun[3];
- X#endif
- Xdouble logcpu;
-
- Xstruct vmtotal total;
-
- Xstruct proc *proc;
- Xstruct proc *pbase;
- Xint bytes;
- Xint initialized = No;
- Xchar *myname = "top";
- Xjmp_buf jmp_int;
- Xchar input_waiting = No;
-
- X/* routines that don't return int */
-
- Xstruct passwd *getpwent();
- Xchar *username();
- Xchar *itoa();
- Xchar *ctime();
- Xchar *rindex();
- Xchar *kill_procs();
- Xchar *renice_procs();
-
- Xint proc_compar();
- Xlong time();
-
- X/* different routines for displaying the user's identification */
- X/* (values assigned to get_userid) */
- Xchar *username();
- Xchar *itoa7();
-
- X/* display routines that need to be predeclared */
- Xint i_loadave();
- Xint u_loadave();
- Xint i_procstates();
- Xint u_procstates();
- Xint i_cpustates();
- Xint u_cpustates();
- Xint i_memory();
- Xint u_memory();
- Xint i_header();
- Xint u_header();
- Xint i_process();
- Xint u_process();
-
- X/* pointers to display routines */
- Xint (*d_loadave)() = i_loadave;
- Xint (*d_procstates)() = i_procstates;
- Xint (*d_cpustates)() = i_cpustates;
- Xint (*d_memory)() = i_memory;
- Xint (*d_header)() = i_header;
- Xint (*d_process)() = i_process;
-
- X/* buffer of proc information lines for display updating */
- X/* unfortunate that this must be declared globally */
- Xchar (* screenbuf)[Display_width];
-
- Xmain(argc, argv)
-
- Xint argc;
- Xchar *argv[];
-
- X{
- X register struct proc *pp;
- X register struct proc **prefp;
- X register int i;
- X register int active_procs;
- X register int change;
-
- X static struct proc **pref;
- X static char tempbuf1[50];
- X static char tempbuf2[50];
- X int total_procs;
- X int old_sigmask;
- X int proc_brkdn[7];
- X int topn = Default_TOPN;
- X int delay = Default_DELAY;
- X int displays = 0; /* indicates unspecified */
- X long curr_time;
- X char *(*get_userid)() = username;
- X char *uname_field = "USERNAME";
- X#ifndef FOUR_ONE
- X char ch;
- X char msg_showing = 0;
- X int readfds;
- X struct timeval timeout;
- X#endif
- X char dostates = No;
- X char do_unames = Yes;
- X char do_init = No;
- X char interactive = Maybe;
- X char show_sysprocs = No;
- X char topn_specified = No;
- X char warnings = 0;
-
- X /* set the buffer for stdout */
- X setbuffer(stdout, stdoutbuf, Buffersize);
-
- X /* get our name */
- X if (argc > 0)
- X {
- X if ((myname = rindex(argv[0], '/')) == 0)
- X {
- X myname = argv[0];
- X }
- X else
- X {
- X myname++;
- X }
- X }
-
- X /* process options */
- X while ((i = getopt(argc, argv, "Sbinus:d:")) != EOF)
- X {
- X switch(i)
- X {
- X case 'u': /* display uid instead of name */
- X do_unames = No;
- X uname_field = " UID ";
- X get_userid = itoa7;
- X break;
-
- X case 'S': /* show system processes */
- X show_sysprocs = Yes;
- X break;
-
- X case 'i': /* go interactive regardless */
- X interactive = Yes;
- X break;
-
- X case 'n': /* batch, or non-interactive */
- X case 'b':
- X interactive = No;
- X break;
-
- X case 'd': /* number of displays to show */
- X if ((i = atoiwi(optarg)) == Invalid || i == 0)
- X {
- X fprintf(stderr,
- X "%s: warning: display count should be positive -- option ignored\n",
- X myname);
- X warnings++;
- X }
- X else
- X {
- X displays = i;
- X }
- X break;
-
- X case 's':
- X if ((delay = atoi(optarg)) < 0)
- X {
- X fprintf(stderr,
- X "%s: warning: seconds delay should be non-negative -- using default\n",
- X myname);
- X delay = Default_DELAY;
- X warnings++;
- X }
- X break;
-
- X default:
- X fprintf(stderr,
- X "Usage: %s [-Sbinu] [-d x] [-s x] [number]\n",
- X myname);
- X exit(1);
- X }
- X }
-
- X /* get count of top processes to display (if any) */
- X if (optind < argc)
- X {
- X if ((topn = atoiwi(argv[optind])) == Invalid)
- X {
- X fprintf(stderr,
- X "%s: warning: process display count should be non-negative -- using default\n",
- X myname);
- X topn = Default_TOPN;
- X warnings++;
- X }
- X else
- X {
- X topn_specified = Yes;
- X }
- X }
-
- X /* initialize the kernel memory interface */
- X init_kernel();
-
- X if (initialized != 1)
- X {
- X /* get the list of symbols we want to access in the kernel */
- X /* errno = 0; ??? */
- X nlist(VMUNIX, nlst);
- X if (nlst[0].n_type == 0)
- X {
- X fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
- X exit(2);
- X }
- X
- X /* get the symbol values out of kmem */
- X getkval(nlst[X_PROC].n_value, &proc, sizeof(int),
- X nlst[X_PROC].n_name);
- X getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int),
- X nlst[X_NPROC].n_name);
- X getkval(nlst[X_HZ].n_value, &hz, sizeof(int),
- X nlst[X_HZ].n_name);
- X getkval(nlst[X_CCPU].n_value, &ccpu, sizeof(int),
- X nlst[X_CCPU].n_name);
- X
- X /* some calculations we use later */
- X
- X mpid_offset = nlst[X_MPID].n_value;
- X avenrun_offset = nlst[X_AVENRUN].n_value;
- X cp_time_offset = nlst[X_CP_TIME].n_value;
- X total_offset = nlst[X_TOTAL].n_value;
- X
- X /* this is used in calculating WCPU -- calculate it ahead of time */
- X#ifdef sun
- X logcpu = log((double)ccpu / FSCALE);
- X#else
- X logcpu = log(ccpu);
- X#endif
- X
- X /* allocate space for proc structure array and array of pointers */
- X bytes = nproc * sizeof(struct proc);
- X pbase = (struct proc *)sbrk(bytes);
- X pref = (struct proc **)sbrk(nproc * sizeof(struct proc *));
-
- X /* Just in case ... */
- X if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
- X {
- X fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
- X exit(3);
- X }
- X
- X /* initialize the hashing stuff */
- X if (do_unames)
- X {
- X init_hash();
- X }
- X
- X if (do_init)
- X {
- X initialized = 1;
- X kill(0, SIGQUIT);
- X exit(99);
- X }
- X }
-
- X /* initialize termcap */
- X init_termcap();
-
- X /*
- X * Smart terminals can only display so many processes, precisely
- X * "screen_length - Header_lines". When run on dumb terminals, nothing
- X * fancy is done anyway, so we can display as many processes as the
- X * system can make. But since we never need to remember what is on the
- X * screen, we only allocate a buffer for one screen line.
- X */
- X if (smart_terminal)
- X {
- X /* can only display (screen_length - Header_lines) processes */
- X i = screen_length - Header_lines;
- X if (topn > i) /* false even when topn == Infinity */
- X {
- X fprintf(stderr,
- X "%s: warning: this terminal can only display %d processes.\n",
- X myname, screen_length - Header_lines);
- X topn = i;
- X warnings++;
- X }
- X }
- X else
- X {
- X i = 1;
- X screen_length = nproc + Header_lines;
- X }
-
- X /* allocate space for the screen buffer */
- X screenbuf = (char (*)[])sbrk(i * Display_width);
- X if (screenbuf == (char (*)[])NULL)
- X {
- X fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
- X exit(4);
- X }
-
- X /* adjust for topn == Infinity */
- X if (topn == Infinity)
- X {
- X /*
- X * For smart terminals, infinity really means everything that can
- X * be displayed (which just happens to be "i" at this point).
- X * On dumb terminals, infinity means every process in the system!
- X * We only really want to do that if it was explicitly specified.
- X * This is always the case when "Default_TOPN != Infinity". But if
- X * topn wasn't explicitly specified and we are on a dumb terminal
- X * and the default is Infinity, then (and only then) we use
- X * "Nominal_TOPN" instead.
- X */
- X#if Default_TOPN == Infinity
- X topn = smart_terminal ? i :
- X (topn_specified ? nproc : Nominal_TOPN);
- X#else
- X topn = smart_terminal ? i : nproc;
- X#endif
- X }
-
- X /* determine interactive state */
- X if (interactive == Maybe)
- X {
- X interactive = smart_terminal;
- X }
-
- X /* if # of displays not specified, fill it in */
- X if (displays == 0)
- X {
- X displays = smart_terminal ? Infinity : 1;
- X }
-
- X /* hold interrupt signals while setting up the screen and the handlers */
- X#ifndef FOUR_ONE
- X old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
- X#endif
- X init_screen();
- X signal(SIGINT, leave);
- X signal(SIGQUIT, leave);
- X signal(SIGTSTP, tstop);
- X#ifndef FOUR_ONE
- X sigsetmask(old_sigmask);
- X#endif
- X if (warnings)
- X {
- X fprintf(stderr, "....");
- X fflush(stderr); /* why must I do this? */
- X sleep(3 * warnings);
- X }
- X clear();
-
- X /* setup the jump buffer for stops */
- X if (setjmp(jmp_int) != 0)
- X {
- X /* control ends up here after an interrupt */
- X clear();
- X reset_display();
- X }
-
- X /*
- X * main loop -- repeat while display count is positive or while it
- X * indicates infinity (by being -1)
- X */
-
- X while ((displays == -1) || (displays-- > 0))
- X {
- X /* read all the proc structures in one fell swoop */
- X getkval(proc, pbase, bytes, "proc array");
-
- X /* get the cp_time array */
- X getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");
-
- X /* get load average array */
- X getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun");
-
- X /* get mpid -- process id of last process */
- X getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
-
- X /* get total -- systemwide main memory usage structure */
- X getkval(total_offset, &total, sizeof(total), "_total");
-
- X /* count up process states and get pointers to interesting procs */
- X total_procs = 0;
- X active_procs = 0;
- X bzero(proc_brkdn, sizeof(proc_brkdn));
- X prefp = pref;
- X for (pp = pbase, i = 0; i < nproc; pp++, i++)
- X {
- X /*
- X * Place pointers to each valid proc structure in pref[].
- X * Process slots that are actually in use have a non-zero
- X * status field. Processes with SSYS set are system
- X * processes---these get ignored unless show_sysprocs is set.
- X */
- X if (pp->p_stat != 0 &&
- X (show_sysprocs || ((pp->p_flag & SSYS) == 0)))
- X {
- X total_procs++;
- X proc_brkdn[pp->p_stat]++;
- X if (pp->p_stat != SZOMB)
- X {
- X *prefp++ = pp;
- X active_procs++;
- X }
- X }
- X }
-
- X /* display the load averages */
- X (*d_loadave)(mpid, avenrun);
-
- X /*
- X * Display the current time.
- X * "ctime" always returns a string that looks like this:
- X *
- X * Sun Sep 16 01:03:52 1973
- X * 012345678901234567890123
- X * 1 2
- X *
- X * We want indices 11 thru 18 (length 8).
- X */
-
- X curr_time = time(0);
- X if (smart_terminal)
- X {
- X Move_to(screen_width - 8, 0);
- X }
- X else
- X {
- X fputs(" ", stdout);
- X }
- X printf("%-8.8s\n", &(ctime(&curr_time)[11]));
-
- X /* display process state breakdown */
- X (*d_procstates)(total_procs, proc_brkdn);
-
- X /* calculate percentage time in each cpu state */
- X if (dostates) /* but not the first time */
- X {
- X total_change = 0;
- X for (i = 0; i < CPUSTATES; i++)
- X {
- X /* calculate changes for each state and overall change */
- X if (cp_time[i] < cp_old[i])
- X {
- X /* this only happens when the counter wraps */
- X change = (int)
- X ((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
- X }
- X else
- X {
- X change = cp_time[i] - cp_old[i];
- X }
- X total_change += (cp_change[i] = change);
- X cp_old[i] = cp_time[i];
- X }
- X (*d_cpustates)(cp_change, total_change);
- X }
- X else
- X {
- X /* we'll do it next time */
- X if (smart_terminal)
- X {
- X z_cpustates();
- X }
- X else
- X {
- X putchar('\n');
- X }
- X dostates = Yes;
- X bzero(cp_old, sizeof(cp_old));
- X }
-
- X /* display main memory statistics */
- X (*d_memory)(
- X pagetok(total.t_rm), pagetok(total.t_arm),
- X pagetok(total.t_vm), pagetok(total.t_avm),
- X pagetok(total.t_free));
-
- X i = 0;
- X if (topn > 0)
- X {
- X /* update the header area */
- X (*d_header)(uname_field);
- X
- X /* sort by cpu percentage (pctcpu) */
- X qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
- X
- X /* adjust for a lack of processes */
- X if (active_procs > topn)
- X {
- X active_procs = topn;
- X }
-
- X /*
- X * Now, show the top "n" processes. The method is slightly
- X * different for dumb terminals, so we will just use two very
- X * similar loops; this increases speed but also code size.
- X */
- X if (smart_terminal)
- X {
- X for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
- X {
- X pp = *prefp;
- X (*d_process)(i, pp, get_userid);
- X }
- X }
- X else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
- X {
- X pp = *prefp;
- X /* (only one buffer lien with dumb terminals) */
- X (*d_process)(0, pp, get_userid);
- X }
- X }
-
- X /* do end-screen processing */
- X u_endscreen(i);
-
- X /* now, flush the output buffer */
- X fflush(stdout);
-
- X /* only do the rest if we have more displays to show */
- X if (displays)
- X {
- X /* switch out for new display on smart terminals */
- X if (smart_terminal)
- X {
- X d_loadave = u_loadave;
- X d_procstates = u_procstates;
- X d_cpustates = u_cpustates;
- X d_memory = u_memory;
- X d_header = u_header;
- X d_process = u_process;
- X }
- X
- X#ifndef FOUR_ONE
- X if (!interactive)
- X#endif
- X {
- X /* set up alarm */
- X signal(SIGALRM, onalrm);
- X alarm(delay);
- X
- X /* wait for the rest of it .... */
- X pause();
- X }
- X#ifndef FOUR_ONE
- X else
- X {
- X /* wait for either input or the end of the delay period */
- X readfds = 1; /* for standard input */
- X timeout.tv_sec = delay;
- X timeout.tv_usec = 0;
- X if (select(32, &readfds, 0, 0, &timeout) > 0)
- X {
- X int newval;
- X char *errmsg;
- X
- X /* something to read -- clear the message area first */
- X if (msg_showing)
- X {
- X if (smart_terminal)
- X {
- X putcap(clear_line);
- X }
- X msg_showing = No;
- X }
-
- X /* now read it and act on it */
- X read(0, &ch, 1);
- X switch(ch)
- X {
- X case '\f': /* redraw screen */
- X reset_display();
- X clear();
- X break;
-
- X case ' ': /* merely update display */
- X break;
- X
- X case 'q': /* quit */
- X quit(0);
- X break;
- X
- X case 'h': /* help */
- X case '?':
- X reset_display();
- X clear();
- X show_help();
- X standout("Hit any key to continue: ");
- X fflush(stdout);
- X read(0, &ch, 1);
- X clear();
- X break;
- X
- X case 'e': /* show errors */
- X if (error_count() == 0)
- X {
- X standout(" Currently no errors to report.");
- X msg_showing = Yes;
- X }
- X else
- X {
- X reset_display();
- X clear();
- X show_errors();
- X standout("Hit any key to continue: ");
- X fflush(stdout);
- X read(0, &ch, 1);
- X clear();
- X }
- X break;
- X
- X case 'n': /* new number */
- X case '#':
- X standout("Number of processes to show: ");
- X newval = readline(tempbuf1, 8, Yes);
- X putchar('\r');
- X if (newval > -1)
- X {
- X if (newval > (i = screen_length - Header_lines))
- X {
- X standout(
- X " This terminal can only display %d processes.",
- X i);
- X newval = i;
- X msg_showing = Yes;
- X break;
- X }
- X
- X if (newval > topn)
- X {
- X /* zero fill appropriate part of screenbuf */
- X bzero(screenbuf[topn],
- X (newval - topn) * Display_width);
- X
- X /* redraw header if need be */
- X if (topn == 0)
- X {
- X d_header = i_header;
- X }
- X }
- X topn = newval;
- X }
- X putcap(clear_line);
- X break;
- X
- X case 's': /* new seconds delay */
- X standout("Seconds to delay: ");
- X if ((i = readline(tempbuf1, 8, Yes)) > -1)
- X {
- X delay = i;
- X }
- X putchar('\r');
- X putcap(clear_line);
- X break;
- X
- X case 'd': /* change display count */
- X standout("Displays to show (currently %s): ",
- X displays == -1 ? "infinite" :
- X itoa(displays));
- X if ((i = readline(tempbuf1, 10, Yes)) > 0)
- X {
- X displays = i;
- X }
- X else if (i == 0)
- X {
- X quit(0);
- X }
- X putchar('\r');
- X putcap(clear_line);
- X break;
-
- X case 'k': /* kill program */
- X fputs("kill ", stdout);
- X if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
- X {
- X if ((errmsg = kill_procs(tempbuf2)) != NULL)
- X {
- X putchar('\r');
- X standout(errmsg);
- X }
- X msg_showing = Yes;
- X }
- X else
- X {
- X putchar('\r');
- X }
- X putcap(clear_line);
- X break;
- X
- X case 'r': /* renice program */
- X fputs("renice ", stdout);
- X if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
- X {
- X if ((errmsg = renice_procs(tempbuf2)) != NULL)
- X {
- X putchar('\r');
- X standout(errmsg);
- X msg_showing = Yes;
- X }
- X }
- X else
- X {
- X putchar('\r');
- X }
- X putcap(clear_line);
- X break;
- X
- X default:
- X standout(" Command not understood");
- X msg_showing = Yes;
- X }
- X }
- X else if (msg_showing)
- X {
- X if (smart_terminal)
- X {
- X putcap(clear_line);
- X }
- X msg_showing = No;
- X }
- X }
- X#endif
- X }
- X }
-
- X quit(0);
- X}
-
- X/*
- X * reset_display() - reset all the display routine pointers so that entire
- X * screen will get redrawn.
- X */
-
- Xreset_display()
-
- X{
- X d_loadave = i_loadave;
- X d_procstates = i_procstates;
- X d_cpustates = i_cpustates;
- X d_memory = i_memory;
- X d_header = i_header;
- X d_process = i_process;
- X}
-
- Xreadline(buffer, size, numeric)
-
- Xchar *buffer;
- Xint size;
- Xint numeric;
-
- X{
- X register char *ptr = buffer;
- X register char ch;
- X register char cnt = 0;
-
- X size -= 1;
- X while ((fflush(stdout), read(0, ptr, 1) > 0))
- X {
- X if ((ch = *ptr) == '\n')
- X {
- X break;
- X }
-
- X if (ch == ch_kill)
- X {
- X *buffer = '\0';
- X return(-1);
- X }
- X else if (ch == ch_erase)
- X {
- X if (cnt <= 0)
- X {
- X putchar('\7');
- X }
- X else
- X {
- X fputs("\b \b", stdout);
- X ptr--;
- X cnt--;
- X }
- X }
- X else if (cnt == size || (numeric && (ch < '0' || ch > '9')))
- X {
- X putchar('\7');
- X }
- X else
- X {
- X putchar(ch);
- X ptr++;
- X cnt++;
- X }
- X }
- X *ptr = '\0';
- X return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
- X}
-
- X/*
- X * signal handlers
- X */
-
- Xleave() /* exit under normal conditions -- INT handler */
-
- X{
- X end_screen();
- X exit(0);
- X}
-
- Xtstop()
-
- X{
- X /* move to the lower left */
- X end_screen();
- X fflush(stdout);
-
- X#ifdef FOUR_ONE /* a 4.1 system */
-
- X /* send a STOP (uncatchable) to everyone in the process group */
- X kill(0, SIGSTOP);
-
- X /* reset the signal handler */
- X signal(SIGTSTP, tstop);
-
- X#else /* assume it is a 4.2 system */
-
- X /* default the signal handler action */
- X signal(SIGTSTP, SIG_DFL);
-
- X /* unblock the signal and send ourselves one */
- X sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
- X kill(0, SIGTSTP);
-
- X /* reset the signal handler */
- X signal(SIGTSTP, tstop);
-
- X#endif
- X /* reinit screen */
- X reinit_screen();
-
- X /* jump to appropriate place */
- X longjmp(jmp_int, 1);
-
- X /*NOTREACHED*/
- X}
-
- Xquit(status) /* exit under duress */
-
- Xint status;
-
- X{
- X end_screen();
- X exit(status);
- X}
-
- Xonalrm()
-
- X{
- X return(0);
- X}
-
- X/*
- X * proc_compar - comparison function for "qsort"
- X * Compares the resource consumption of two processes using five
- X * distinct keys. The keys (in descending order of importance) are:
- X * percent cpu, cpu ticks, state, resident set size, total virtual
- X * memory usage. The process states are ordered as follows (from least
- X * to most important): WAIT, zombie, sleep, stop, start, run. The
- X * array declaration below maps a process state index into a number
- X * that reflects this ordering.
- X */
-
- Xunsigned char sorted_state[] =
- X{
- X 0, /* not used */
- X 3, /* sleep */
- X 1, /* ABANDONED (WAIT) */
- X 6, /* run */
- X 5, /* start */
- X 2, /* zombie */
- X 4 /* stop */
- X};
- X
- Xproc_compar(pp1, pp2)
-
- Xstruct proc **pp1;
- Xstruct proc **pp2;
-
- X{
- X register struct proc *p1;
- X register struct proc *p2;
- X register int result;
- X#ifndef sun
- X register double dresult;
- X#endif
-
- X /* remove one level of indirection */
- X p1 = *pp1;
- X p2 = *pp2;
-
- X /* compare percent cpu (pctcpu) */
- X#ifdef sun
- X if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
- X#else
- X if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
- X#endif
- X {
- X /* use cpticks to break the tie */
- X if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
- X {
- X /* use process state to break the tie */
- X if ((result = sorted_state[p2->p_stat] -
- X sorted_state[p1->p_stat]) == 0)
- X {
- X /* use priority to break the tie */
- X if ((result = p2->p_pri - p1->p_pri) == 0)
- X {
- X /* use resident set size (rssize) to break the tie */
- X if ((result = p2->p_rssize - p1->p_rssize) == 0)
- X {
- X /* use total memory to break the tie */
- X#ifdef pyr
- X result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
- X (p1->p_tsize + p1->p_dsize + p1->p_ussize);
- X#else
- X result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
- X (p1->p_tsize + p1->p_dsize + p1->p_ssize);
- X#endif
- X }
- X }
- X }
- X }
- X }
- X#ifndef sun
- X else
- X {
- X result = dresult < 0.0 ? -1 : 1;
- X }
- X#endif
-
- X return(result);
- X}
-
- X/* routines to translate uids into a string */
-
- Xchar *user_name(euid, ruid)
-
- Xint euid, ruid;
-
- X{
- X return(username(euid));
- X}
-
- Xchar *user_uid(euid, ruid)
-
- Xint euid, ruid;
-
- X{
- X return(itoa7(euid));
- X}
-
- X/*
- X * These routines handle uid to username mapping.
- X * They use a hashing table scheme to reduce reading overhead.
- X */
-
- Xstruct hash_el {
- X int uid;
- X char name[8];
- X};
-
- X#define H_empty -1
-
- X/* simple minded hashing function */
- X#define hashit(i) ((i) % Table_size)
-
- Xstruct hash_el hash_table[Table_size];
-
- Xinit_hash()
-
- X{
- X register int i;
- X register struct hash_el *h;
-
- X for (h = hash_table, i = 0; i < Table_size; h++, i++)
- X {
- X h->uid = H_empty;
- X }
- X}
-
- Xchar *username(uid)
-
- Xregister int uid;
-
- X{
- X register int index;
- X register int found;
- X register char *name;
-
- X /* This is incredibly naive, but it'll probably get changed anyway */
- X index = hashit(uid);
- X while ((found = hash_table[index].uid) != uid)
- X {
- X if (found == H_empty)
- X {
- X /* not here -- get it out of passwd */
- X index = get_user(uid);
- X break; /* out of while */
- X }
- X index = (index + 1) % Table_size;
- X }
- X return(hash_table[index].name);
- X}
-
- Xenter_user(uid, name)
-
- Xregister int uid;
- Xregister char *name;
-
- X{
- X register int length;
- X register int index;
- X register int try;
- X static int uid_count = 0;
-
- X /* avoid table overflow -- insure at least one empty slot */
- X if (++uid_count >= Table_size)
- X {
- X fprintf(stderr, "table overflow: too many users\n");
- X quit(10);
- X }
-
- X index = hashit(uid);
- X while ((try = hash_table[index].uid) != H_empty)
- X {
- X if (try == uid)
- X {
- X return(index);
- X }
- X index = (index + 1) % Table_size;
- X }
- X hash_table[index].uid = uid;
- X strncpy(hash_table[index].name, name, 8);
- X return(index);
- X}
-
- Xget_user(uid)
-
- Xregister int uid;
-
- X{
- X struct passwd *pwd;
- X register int last_index;
-
- X while ((pwd = getpwent()) != NULL)
- X {
- X last_index = enter_user(pwd->pw_uid, pwd->pw_name);
- X if (pwd->pw_uid == uid)
- X {
- X return(last_index);
- X }
- X }
- X return(enter_user(uid, itoa7(uid)));
- X}
-
- Xatoiwi(str)
-
- Xchar *str;
-
- X{
- X register int len;
-
- X len = strlen(str);
- X if (len != 0)
- X {
- X if (strncmp(str, "infinity", len) == 0 ||
- X strncmp(str, "all", len) == 0 ||
- X strncmp(str, "maximum", len) == 0)
- X {
- X return(Infinity);
- X }
- X else if (str[0] == '-')
- X {
- X return(Invalid);
- X }
- X else
- X {
- X return(atoi(str));
- X }
- X }
- X return(0);
- X}
-
- X/*
- X * itoa - convert integer (decimal) to ascii string for positive numbers
- X * only (we don't bother with negative numbers since we know we
- X * don't use them).
- X */
-
- Xstatic char buffer[16]; /* shared by the next two routines */
-
- Xchar *itoa(val)
-
- Xregister int val;
-
- X{
- X register char *ptr;
-
- X ptr = buffer + sizeof(buffer);
- X *--ptr = '\0';
- X if (val == 0)
- X {
- X *--ptr = '0';
- X }
- X else while (val != 0)
- X {
- X *--ptr = (val % 10) + '0';
- X val /= 10;
- X }
- X return(ptr);
- X}
-
- X/*
- X * itoa7(val) - like itoa, except the number is right justified in a 7
- X * character field. This code is a duplication of itoa instead of
- X * a front end to a more general routine for efficiency.
- X */
-
- Xchar *itoa7(val)
-
- Xregister int val;
-
- X{
- X register char *ptr;
-
- X ptr = buffer + sizeof(buffer);
- X *--ptr = '\0';
- X if (val == 0)
- X {
- X *--ptr = '0';
- X }
- X else while (val != 0)
- X {
- X *--ptr = (val % 10) + '0';
- X val /= 10;
- X }
- X while (ptr > buffer + sizeof(buffer) - 7)
- X {
- X *--ptr = ' ';
- X }
- X return(ptr);
- X}
-
- @//E*O*F top.c//
- chmod u=rw,g=rw,o=rw $OUT
-
- echo x - top.local.h
- if test -f top.local.h ; then
- echo top.local.h exists, putting output in $$top.local.h
- OUT=$$top.local.h
- STATUS=1
- else
- OUT=top.local.h
- fi
- sed 's/^X//' > $OUT <<'@//E*O*F top.local.h//'
- X/*
- X * Top - a top users display for Berkeley Unix
- X *
- X * Definitions for things that might vary between installations.
- X */
-
- X/*
- X * "Table_size" defines the size of the hash tables used to map uid to
- X * username. The number of users in /etc/passwd CANNOT be greater than
- X * this number. If the error message "table overflow: too many users"
- X * is printed by top, then "Table_size" needs to be increased. Things will
- X * work best if the number is a prime number that is about twice the number
- X * of lines in /etc/passwd.
- X */
- X#ifndef Table_size
- X#define Table_size 421
- X#endif
-
- X/*
- X * "Nominal_TOPN" is used as the default TOPN when Default_TOPN is Infinity
- X * and the output is a dumb terminal. If we didn't do this, then
- X * installations who use a default TOPN of Infinity will get every
- X * process in the system when running top on a dumb terminal (or redirected
- X * to a file). Note that Nominal_TOPN is a default: it can still be
- X * overridden on the command line, even with the value "infinity".
- X */
- X#ifndef Nominal_TOPN
- X#define Nominal_TOPN 18
- X#endif
-
- X/*
- X * File name for the system image and the memory devices.
- X */
- X#define VMUNIX "/vmunix"
- X#define KMEM "/dev/kmem"
- X#define MEM "/dev/mem"
- @//E*O*F top.local.h//
- chmod u=rw,g=rw,o=rw $OUT
-
- echo x - top.man
- if test -f top.man ; then
- echo top.man exists, putting output in $$top.man
- OUT=$$top.man
- STATUS=1
- else
- OUT=top.man
- fi
- sed 's/^X//' > $OUT <<'@//E*O*F top.man//'
- X.\" NOTE: changes to the manual page for "top" should be made in the
- X.\" file "top.man" and NOT in the file "top.1".
- X.TH TOP 1 Local
- X.UC 4
- X.SH NAME
- Xtop \- display and update information about the top cpu processes
- X.SH SYNOPSIS
- X.B top
- X[
- X.B \-Sbinu
- X] [
- X.BI \-d count
- X] [
- X.BI \-s time
- X] [
- X.I number
- X]
- X.SH DESCRIPTION
- X.\" This defines appropriate quote strings for nroff and troff
- X.ds lq \&"
- X.ds rq \&"
- X.if t .ds lq ``
- X.if t .ds rq ''
- X.\" Just in case these number registers aren't set yet...
- X.if \nN==0 .nr N 10
- X.if \nD==0 .nr D 5
- X.I Top
- Xdisplays the top
- X.if !\nN==-1 \nN
- Xprocesses on the system and periodically updates this information.
- X.if \nN==-1 \
- X\{\
- XIf standard output is an intelligent terminal (see below) then
- Xas many processes as will fit on the terminal screen are displayed
- Xby default. Otherwise, a good number of them are shown (around 20).
- X.\}
- XRaw cpu percentage is used to rank the processes. If
- X.I number
- Xis given, then the top
- X.I number
- Xprocesses will be displayed instead of the default.
- X.PP
- X.I Top
- Xmakes a distinction between terminals that support advanced capabilities
- Xand those that do not. This
- Xdistinction affects the choice of defaults for certain options. In the
- Xremainder of this document, an \*(lqintelligent\*(rq terminal is one that
- Xsupports cursor addressing, clear screen, and clear to end of line.
- XConversely, a \*(lqdumb\*(rq terminal is one that does not support such
- Xfeatures. If the output of
- X.I top
- Xis redirected to a file, it acts as if it were being run on a dumb
- Xterminal.
- X.SH OPTIONS
- X.TP
- X.B \-S
- XShow system processes in the display. Normally, system processes such as
- Xthe pager and the swapper are not shown. This option makes them visible.
- X.TP
- X.B \-b
- XUse \*(lqbatch\*(rq mode. In this mode, all input from the terminal is
- Xignored. Interrupt characters (such as ^C and ^\e) still have an effect.
- XThis is the default on a dumb terminal, or when the output is not a terminal.
- X.TP
- X.B \-i
- XUse \*(lqinteractive\*(rq mode. In this mode, any input is immediately
- Xread for processing. See the section on \*(lqInteractive Mode\*(rq
- Xfor an explanation of
- Xwhich keys perform what functions. After the command is processed, the
- Xscreen will immediately be updated, even if the command was not
- Xunderstood. This mode is the default when standard output is an
- Xintelligent terminal.
- X.TP
- X.B \-n
- XUse \*(lqnon-interactive\*(rq mode. This is indentical to \*(lqbatch\*(rq
- Xmode.
- X.TP
- X.B \-u
- XDo not take the time to map uid numbers to usernames. Normally,
- X.I top
- Xwill read as much of the file \*(lq/etc/passwd\*(rq as is necessary to map
- Xall the user id numbers it encounters into login names. This option
- Xdisables all that, while possibly decreasing execution time. The uid
- Xnumbers are displayed instead of the names.
- X.TP
- X.BI \-d count
- XShow only
- X.I count
- Xdisplays, then exit. A display is considered to be one update of the
- Xscreen. This option allows the user to select the number of displays he
- Xwants to see before
- X.I top
- Xautomatically exits. For intelligent terminals, no upper limit
- Xis set. The default is 1 for dumb terminals.
- X.TP
- X.BI \-s time
- XSet the delay between screen updates to
- X.I time
- Xseconds. The default delay between updates is \nD seconds.
- X.PP
- XBoth
- X.I count
- Xand
- X.I number
- Xfields can be specified as \*(lqinfinite\*(rq, indicating that they can
- Xstretch as far as possible. This is accomplished by using any proper
- Xprefix of the keywords
- X\*(lqinfinity\*(rq,
- X\*(lqmaximum\*(rq,
- Xor
- X\*(lqall\*(rq.
- XThe default for
- X.I count
- Xon an intelligent terminal is, in fact,
- X.BI infinity .
- X.SH "INTERACTIVE MODE"
- XWhen
- X.I top
- Xis running in \*(lqinteractive mode\*(rq, it reads commands from the
- Xterminal and acts upon them accordingly. In this mode, the terminal is
- Xput in \*(lqCBREAK\*(rq, so that a character will be
- Xprocessed as soon as it is typed. Almost always, a key will be
- Xpressed when
- X.I top
- Xis between displays; that is, while it is waiting for
- X.I time
- Xseconds to elapse. If this is the case, the command will be
- Xprocessed and the display will be updated immediately thereafter
- X(reflecting any changes that the command may have specified). This
- Xhappens even if the command was incorrect. If a key is pressed while
- X.I top
- Xis in the middle of updating the display, it will finish the update and
- Xthen process the command. Some commands require additional information,
- Xand the user will be prompted accordingly. While typing this information
- Xin, the user's erase and kill keys (as set up by the command
- X.IR stty )
- Xare recognized, and a newline terminates the input.
- X.PP
- XThese commands are currently recognized (^L refers to control-L):
- X.TP
- X.B ^L
- XRedraw the screen.
- X.IP "\fBh\fP\ or\ \fB?\fP"
- XDisplay a summary of the commands (help screen).
- X.TP
- X.B q
- XQuit
- X.IR top.
- X.TP
- X.B d
- XChange the number of displays to show (prompt for new number).
- XRemember that the next display counts as one, so typing
- X.B d1
- Xwill make
- X.I top
- Xshow one final display and then immediately exit.
- X.TP
- X.B n or #
- XChange the number of processes to display (prompt for new number).
- X.TP
- X.B s
- XChange the number of seconds to delay between displays
- X(prompt for new number).
- X.TP
- X.B k
- XSend a signal (\*(lqkill\*(rq by default) to a list of processes. This
- Xacts similarly to the command
- X.IR kill (1)).
- X.TP
- X.B r
- XChange the priority (the \*(lqnice\*(rq) of a list of processes.
- XThis acts similarly to the command
- X.IR renice (8)).
- X.TP
- X.B e
- XDisplay a list of system errors (if any) generated by the last
- X.BR k ill
- Xor
- X.BR r enice
- Xcommand.
- X.SH "THE DISPLAY"
- XThe top few lines of the display show general information
- Xabout the state of the system, including
- Xthe last process id assigned to a process,
- Xthe three load averages,
- Xthe current time,
- Xthe number of existing processes,
- Xthe number of processes in each state
- X(sleeping, ABANDONED, running, starting, zombies, and stopped),
- Xand a percentage of time spent in each of the processor states
- X(user, nice, system, and idle).
- XIt also includes the amount of virtual and real memory in use
- X(with the amount of memory considered \*(lqactive\*(rq in parentheses) and
- Xthe amount of free memory.
- X.PP
- XThe remainder of the screen displays information about individual
- Xprocesses. This display is similar in spirit to
- X.IR ps (1)
- Xbut it is not exactly the same. PID is the process id, USERNAME is the name
- Xof the process's owner (if
- X.B \-u
- Xis specified, a UID column will be substituted for USERNAME),
- XPRI is the current priority of the process,
- XNICE is the nice amount (in the range \-20 to 20),
- XSIZE is the total size of the process (text, data, and stack),
- XRES is the current amount of resident memory (both SIZE and RES are
- Xgiven in kilobytes),
- XSTATE is the current state (one of \*(lqsleep\*(rq, \*(lqWAIT\*(rq,
- X\*(lqrun\*(rq, \*(lqidl\*(rq, \*(lqzomb\*(rq, or \*(lqstop\*(rq),
- XTIME is the number of system and user cpu seconds that the process has used,
- XWCPU is the weighted cpu percentage (this is the same value that
- X.IR ps (1)
- Xdisplays as CPU),
- XCPU is the raw percentage and is the field that is sorted to determine
- Xthe order of the processes, and
- XCOMMAND is the name of the command that the process is currently running
- X(if the process is swapped out, this column is marked \*(lq<swapped>\*(rq).
- X.SH NOTES
- XThe \*(lqABANDONED\*(rq state (known in the kernel as \*(lqSWAIT\*(rq) was
- Xabandoned, thus the name. A process should never end up in this state.
- X.SH AUTHOR
- XWilliam LeFebvre, Rice University graduate student
- X.SH FILES
- X.DT
- X/dev/kmem kernel memory
- X.br
- X/dev/mem physical memory
- X.br
- X/etc/passwd used to map uid numbers to user names
- X.br
- X/vmunix system image
- X.SH BUGS
- XThe command name for swapped processes should be tracked down, but this
- Xwould make the program run slower.
- X.PP
- XAs with
- X.IR ps (1),
- Xthings can change while
- X.I top
- Xis collecting information for an update. The picture it gives is only a
- Xclose approximation to reality.
- X.SH "SEE ALSO"
- Xkill(1),
- Xps(1),
- Xstty(1),
- Xmem(4),
- Xrenice(8)
- @//E*O*F top.man//
- chmod u=rw,g=rw,o=rw $OUT
-
- echo x - MANIFEST
- if test -f MANIFEST ; then
- echo MANIFEST exists, putting output in $$MANIFEST
- OUT=$$MANIFEST
- STATUS=1
- else
- OUT=MANIFEST
- fi
- sed 's/^X//' > $OUT <<'@//E*O*F MANIFEST//'
- XChanges 1
- XMANIFEST 2
- XMakefile 1
- XManifest 1
- XREADME 1
- Xboolean.h 1
- Xbzero.c 1
- Xcommands.c 1
- Xdisplay.c 1
- Xgetopt.c 1
- Xkernel.c 1
- Xlayout.h 1
- Xscreen.c 1
- Xscreen.h 1
- Xsigconv.awk 1
- Xtop.c 2
- Xtop.h 1
- Xtop.local.h 2
- Xtop.man 2
- @//E*O*F MANIFEST//
- chmod u=rw,g=rw,o=rw $OUT
-
- echo Inspecting for damage in transit...
- temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
- trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
- cat > $temp <<\!!!
- 1257 3700 26262 top.c
- 36 209 1209 top.local.h
- 247 1361 7861 top.man
- 19 38 589 MANIFEST
- 1559 5308 35921 total
- !!!
- wc top.c top.local.h top.man MANIFEST | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
- if test -s $dtemp ; then
- echo "Ouch [diff of wc output]:"
- cat $dtemp
- STATUS=1
- elif test $STATUS = 0 ; then
- echo "No problems found."
- else
- echo "WARNING -- PROBLEMS WERE FOUND..."
- fi
- exit $STATUS
-